Skip to content

Conversation

@github-actions
Copy link

LeetCode 每日一题:3315. Construct the Minimum Bitwise Array II

题目链接: https://leetcode.com/problems/construct-the-minimum-bitwise-array-ii/
难度: Medium

解题过程

思考过程

我需要先获取题目3315的信息。

思考过程

现在来分析题目:构造最小位运算数组 II。给定一个质数数组 nums,需要构造 ans,使得 ans[i] OR (ans[i] + 1) == nums[i],并且 ans[i] 尽可能小。如果找不到这样的 ans[i],设为 -1。

我们需要找出满足条件的最小 x,使得 x OR (x+1) = target(target 是质数)。

理解这个位运算性质:x OR (x+1) 是什么?我们需要分析二进制模式。

考虑 x 和 x+1 的二进制表示。因为 x+1 是 x 加 1,它们的二进制形式有特定的关系。例如,假设 x 的二进制是 ...011...1(末尾是一串 1),那么 x+1 就是 ...100...0(末尾的 1 变成 0,前一位加 1)。那么 x OR (x+1) 会将 x 的末尾 1 串和 x+1 的进位后的位组合起来。

更具体地说,设 x 的二进制最低的 0 位在位置 k(从 0 开始)。那么 x 在位置 k 是 0,在位置 0 到 k-1 都是 1。则 x+1 会将位置 0 到 k-1 变成 0,位置 k 变成 1,更高位不变。那么 x OR (x+1) 的结果是:位置 0 到 k-1 都是 1,位置 k 是 1(因为 x+1 在该位是 1,x 在该位是 0),更高位与 x 相同(因为 x 和 x+1 在更高位相同)。所以结果就是:从位置 0 到 k 都是 1,更高位与 x 相同。

换句话说,x OR (x+1) 将 x 的最低有效 0 位及其以下所有位都设置为 1。

设 x 的二进制为 ...b_{k+1} b_k b_{k-1} ... b_0,其中 b_k = 0,且对于所有 i < k,b_i = 1。那么 x OR (x+1) = ...b_{k+1} 1 1 ... 1(k+1 个低位都是 1)。

因此,给定目标 target,我们需要找到 x 使得 target 的二进制表示中,从最低位开始,直到某个 0 位(包括该位)都是 1,而更高位与 target 相同。也就是说,target 必须满足:存在一个位置 k,使得 target 的低 k+1 位全是 1,且第 k 位在原始 x 中是 0(但在 target 中是 1),更高位与 x 相同。

实际上,target 的二进制表示中,最低的 0 位(从低位开始找第一个 0)决定了 k。但 target 本身可能没有 0 位(即全 1),那么 k 就是最高位之外?让我们思考。

我们需要找到 x,使得 x OR (x+1) = target。设 target 的二进制为 T。令 k 为 T 中最低的 0 位的位置(从 0 开始)。如果 T 所有位都是 1(即 target = 2^m - 1),那么不存在这样的 k?实际上,如果 target 全为 1,那么对于任意 x,x OR (x+1) 能产生全 1 吗?是的,如果 x 本身是全 1,那么 x+1 会导致进位,但 x OR (x+1) 可能不是全 1?例如,x = 3 (11),x+1 = 4 (100),OR 结果是 111 = 7,全 1。所以 target 全为 1 是可能的。但此时 k 应该是多少?在 x 中,最低的 0 位是位置 2(因为 x=11 二进制,位 2 是 0),但 target 全为 1,所以 target 没有 0 位。所以我们需要另一种分析方法。

更系统的方法:对于给定的 target,我们想找到最小的 x 满足 x OR (x+1) = target。

观察:x OR (x+1) = target 意味着 target 的二进制表示中,所有为 0 的位在 x 和 x+1 中都必须为 0。但 x 和 x+1 的 OR 结果为 1 的位,至少有一个为 1。

实际上,我们可以通过位运算推导出 x。设 target = T。由于 x OR (x+1) = T,那么 x 必须满足:对于 T 中为 0 的位,x 和 x+1 在该位都必须为 0。对于 T 中为 1 的位,x 或 x+1 至少有一个为 1。

此外,x 和 x+1 的关系是固定的:x+1 = x + 1。

我们可以尝试直接构造 x。假设我们知道 x 的最低有效 0 位在位置 k(即 x 的第 k 位是 0,且所有更低有效位都是 1)。那么 x 的形式为:...b_{k+1} 0 111...1(k 个低位是 1)。那么 x+1 = ...b_{k+1} 1 000...0。OR 结果为 ...b_{k+1} 1 111...1(k+1 个低位是 1)。因此 T 的低 k+1 位全是 1,第 k+1 位及以上与 b_{k+1} 相同。

因此,给定 T,我们需要找到 k,使得 T 的低 k+1 位全是 1,且第 k+1 位(如果存在)是 0?不,第 k+1 位可以是任意值,但必须与 x 的第 k+1 位相同。实际上,x 的第 k+1 位就是 T 的第 k+1 位,因为 OR 操作不会改变该位(x 和 x+1 在该位相同,都是 b_{k+1})。所以 T 的第 k+1 位可以是 0 或 1。

因此算法:对于每个 target T,我们需要找到最大的 k,使得 T 的低 k+1 位全是 1(即 T & ((1 << (k+1)) - 1) == (1 << (k+1)) - 1)。然后 x 的低 k 位是 1,第 k 位是 0,更高位与 T 相同。即 x = (T & ~((1 << (k+1)) - 1)) | ((1 << k) - 1)。但需要确保 x 的第 k 位是 0,这已经满足。

但我们需要最小的 x。对于给定的 T,可能存在多个 k 满足条件(因为 T 可能有多个后缀全为 1 的段)。例如 T = 7 (111),低 1 位全 1?实际上低 1 位是 1,低 2 位是 11,低 3 位是 111。所以 k 可以是 0,1,2。每个 k 对应一个 x:

  • k=0:低 1 位全 1,x 的低 0 位是 1?公式:x = (T & ~((1 << 1)-1)) | ((1 << 0)-1) = (7 & ~1) | 0 = (7 & ~1) = 6? 7 & ~1 = 110 = 6。x=6。检查:6 OR 7 = 7,正确。
  • k=1:低 2 位全 1,x = (7 & ~3) | 1 = (7 & ~3) = 4? 7 & ~3 = 4 (100)。x=4。检查:4 OR 5 = 5? 4 OR 5 = 5 (101),不等于 7。等等,计算错误。实际上 4 OR 5 = 101 = 5,不是 7。所以 k=1 不满足?为什么?因为低 2 位全 1 是满足的,但 x 的构造公式可能有问题。

让我们重新推导公式。如果 T 的低 k+1 位全为 1,那么 x 应该为:将 T 的第 k 位设为 0,并将所有更低位置为 1。即 x = T,但将第 k 位设为 0,并确保所有更低位为 1。但 T 的低 k 位已经是 1,所以只需将第 k 位设为 0。所以 x = T 且第 k 位清零:x = T & ~(1 << k)。但需要确保 x 的低 k 位是 1,这已经满足(因为 T 的低 k 位是 1)。验证:对于 T=7,k=0:x = 7 & ~1 = 6。正确。k=1:x = 7 & ~2 = 5。5 OR 6 = 7?5 (101) OR 6 (110) = 111 = 7,正确!所以 k=1 也满足。k=2:x = 7 & ~4 = 3。3 OR 4 = 7,正确。所以实际上对于 T=7,有三个可能的 x:6,5,3。我们需要最小的 x,即 3。

因此,对于给定的 T,我们需要找到所有满足条件的 k(即 T 的低 k+1 位全为 1),然后计算 x = T & ~(1 << k),并取最小的 x。

但 k 可以是多少?最大可能的 k 是多少?k 从 0 到最高位减 1?实际上,如果 T 的所有位都是 1,那么 k 可以取 0 到 bit_length-1。但我们需要最小的 x,所以我们应该选择最大的 k?因为 x = T & ~(1 << k),随着 k 增大,被清零的位越高,x 越小(因为高位清零会大幅减小数值)。所以为了得到最小的 x,我们应该选择最大的满足条件的 k。

但需要确保 x 是有效的,即 x OR (x+1) = T。我们已经推导出条件:T 的低 k+1 位必须全为 1。那么是否还需要其他条件?实际上,只要 T 的低 k+1 位全为 1,那么 x = T & ~(1 << k) 就满足条件。验证:x 的第 k 位是 0,低 k 位是 1。那么 x+1 的第 k 位是 1,低 k 位是 0。OR 结果:低 k 位是 1,第 k 位是 1,更高位与 T 相同。因为 T 的低 k+1 位全是 1,所以 OR 结果与 T 相同。

因此算法:对于每个 target T,找到最大的 k,使得 T 的低 k+1 位全为 1。如果不存在这样的 k(即 T 的最低位是 0),那么无解(因为低 1 位必须是 1)。实际上,如果 T 的最低位是 0,那么低 1 位不是 1,所以 k 至少为 0 就不满足。因此无解,返回 -1。

但 T 是质数,除了 2 以外都是奇数,所以最低位通常是 1(除了 2)。对于 2,二进制为 10,低 1 位是 0,所以无解。确实示例中 nums[0]=2 返回 -1。

现在需要找到最大的 k,使得低 k+1 位全为 1。等价于找到 T 中最长的后缀连续 1 的长度。设 suffix_ones = 从最低位开始的连续 1 的个数。那么 k 可以取 0 到 suffix_ones-1。最大 k = suffix_ones-1。因为需要低 k+1 位全为 1,k 最大为 suffix_ones-1(因为 suffix_ones 是连续 1 的长度,所以低 suffix_ones 位全是 1)。那么 k = suffix_ones-1 满足条件。

因此 x = T & ~(1 << (suffix_ones-1))。

但 suffix_ones 可能为 0(如果最低位是 0),此时无解。

验证示例:

  1. nums = [2,3,5,7]
  • T=2 (10): suffix_ones = 0(最低位是 0),无解,返回 -1。
  • T=3 (11): suffix_ones = 2(二进制 11),k = 1,x = 3 & ~(1<<1) = 3 & ~2 = 3 & 1 = 1?3 二进制 11,清除第 1 位(从 0 开始)得到 01 = 1。正确。
  • T=5 (101): suffix_ones = 1(最低位是 1,次低位是 0),k = 0,x = 5 & ~1 = 4。正确。
  • T=7 (111): suffix_ones = 3,k = 2,x = 7 & ~4 = 3。正确。
  1. nums = [11,13,31]
  • T=11 (1011): 二进制 1011,最低位开始连续 1 的个数:最低位 1,次低位 1,第三位 0,所以 suffix_ones = 2。k=1,x = 11 & ~2 = 11 & ~2 = 1011 & ~0010 = 1011 & 1101 = 1001 = 9。正确。
  • T=13 (1101): 二进制 1101,连续 1 的个数:最低位 1,次低位 0,所以 suffix_ones = 1。k=0,x = 13 & ~1 = 12。正确。
  • T=31 (11111): 二进制 11111,连续 1 的个数 = 5。k=4,x = 31 & ~16 = 15。正确。

因此算法正确。

现在我们需要为每个 nums[i] 计算 suffix_ones。可以通过循环计算:当 (T & 1) == 1 时,右移并计数。但注意 T 最大为 1e9,位数不超过 30,所以 O(log T) 时间足够。

但还需要注意:质数除了 2 都是奇数,所以 suffix_ones 至少为 1(除了 2)。但可能存在 suffix_ones = 1 的情况,此时 k=0,x = T & ~1 = T-1(因为最低位清零)。例如 T=5,x=4;T=13,x=12。正确。

现在我们需要实现算法。

复杂度:对于每个数 O(log T),总 O(n log MAX)。

现在检查边界情况:如果 suffix_ones = 0,返回 -1。否则计算 mask = 1 << (suffix_ones-1),x = T & ~mask。

但需要确保 x 是正整数。因为 T 是质数,大于等于 2,所以 x 可能为 0?例如 T=3,suffix_ones=2,mask=2,x=1,大于 0。T=2 无解。T=2^m-1 形式(全 1),x 可能为 0?例如 T=1?但 T 最小为 2,且是质数,所以 T=3,7,31 等,x 不会是 0。

现在我们需要生成代码。根据题目,这是一个普通题目,使用 SolutionBase 模式。类名是 ConstructTheMinimumBitwiseArrayIiSolution,基类是 ConstructTheMinimumBitwiseArrayIi,命名空间是 problem_3315。

我们需要实现 minBitwiseArray 方法。可以提供一个策略:直接计算 suffix_ones。

也许可以添加其他策略,但似乎只有一种自然算法。不过可以尝试其他方法,比如位操作技巧。

另一种策略:直接计算 x = T ^ (1 << (suffix_ones-1))?因为 T & ~mask 等价于 T ^ mask?当 mask 位在 T 中为 1 时,异或会将其清零,确实相同。但需要确保 mask 位在 T 中为 1,由于 suffix_ones 定义,该位确实是 1。所以 x = T ^ (1 << (suffix_ones-1))。

因此可以有两种实现方式,但本质相同。

还可以考虑数学解法:x = T - (1 << suffix_ones)?验证:T=7,suffix_ones=3,1<<3=8,T-8=-1,不对。应该是 x = T - (1 << (suffix_ones-1))?T=7,suffix_ones=3,1<<2=4,T-4=3,正确。但需要确保 T 的低 suffix_ones 位全为 1。实际上,如果低 suffix_ones 位全为 1,那么 T 可以表示为:高部分后面跟着 suffix_ones 个 1。设 high = T >> suffix_ones,那么 T = (high << suffix_ones) | ((1 << suffix_ones) - 1)。那么 x = (high << suffix_ones) | ((1 << (suffix_ones-1)) - 1)。即 x = T - (1 << (suffix_ones-1))。因为减去第 suffix_ones-1 位的 1。所以 x = T - (1 << (suffix_ones-1))。验证:T=7,suffix_ones=3,1<<2=4,T-4=3。T=11,suffix_ones=2,1<<1=2,T-2=9。正确。所以更简单:x = T - (1 << (suffix_ones-1))。

因此算法:计算 suffix_ones,如果为 0 返回 -1,否则返回 T - (1 << (suffix_ones-1))。

但需要注意:suffix_ones 可能达到 30(因为 T <= 1e9 < 2^30)。1 << (suffix_ones-1) 在 int 范围内。

现在实现。

让我们先编写头文件、源文件和测试文件。

根据示例,头文件应包含 SolutionBase 定义。但题目给的代码模板是 class Solution { public: vector minBitwiseArray(vector& nums); }。但参考示例使用的是 SolutionBase 模式。我们需要按照给定的 solution_class_name 和 solution_class_base 来命名。查看问题信息:solution_class_name 是 ConstructTheMinimumBitwiseArrayIiSolution,solution_class_base 是 ConstructTheMinimumBitwiseArrayIi。命名空间是 problem_3315。

因此我们需要创建一个继承自 SolutionBase 的类,基类名为 ConstructTheMinimumBitwiseArrayIi。参考 TwoSum 示例:TwoSumSolution 继承自 SolutionBase,其中 Func 是 std::function<vector(vector&, int)>。对于本题,函数签名是 vector minBitwiseArray(vector& nums)。所以 Func 应该是 std::function<vector(vector&)>。

因此头文件定义 Func 类型和类。然后源文件实现多个策略(至少一个)。测试文件使用参数化测试。

现在生成代码。

但在此之前,由于不涉及数据结构,不需要调用 get_data_structure_implementation。

现在生成头文件。

思考过程

现在编译项目以检查代码是否正确。

思考过程

现在运行测试以确保通过所有测试用例。

思考过程

很好,所有测试通过。现在我们可以提供解题报告。

解题报告

题目理解

题目要求构造一个数组 ans,使得对于每个索引 i,满足 ans[i] OR (ans[i] + 1) == nums[i],并且 ans[i] 尽可能小。如果不存在这样的 ans[i],则设为 -1。

关键观察:对于给定的目标值 T = nums[i],我们需要找到最小的非负整数 x,使得 x OR (x + 1) = T

位运算性质分析

考虑 xx+1 的二进制表示。设 x 的最低有效 0 位位于位置 k(从 0 开始计数),则:

  • x 的第 k 位为 0,所有更低位(0 到 k-1)均为 1。
  • x+1 会将 x 的低 k 位从 1 变为 0,并将第 k 位变为 1。

因此,x OR (x+1) 的结果是:

  • k 位全为 1(来自 x),
  • k 位为 1(来自 x+1),
  • 更高位与 x 相同(因为 xx+1 在这些位上相同)。

换句话说,x OR (x+1)x 的最低有效 0 位及其以下所有位都设置为 1。

算法推导

给定目标值 T,我们需要找到 k,使得 T 的低 k+1 位全为 1。设 suffix_onesT 的二进制表示中从最低位开始的连续 1 的个数。那么:

  • 如果 suffix_ones = 0,即 T 的最低位为 0,则无解,返回 -1。
  • 否则,取最大的 k = suffix_ones - 1,则 x 可以通过将 T 的第 k 位清零得到:
    [
    x = T & \sim (1 \ll k) = T - (1 \ll k)
    ]
    这个 x 就是满足条件的最小值,因为更大的 k 会使清零的位更高,从而得到更小的 x

复杂度分析

  • 时间复杂度:O(n log MAX),其中 n 是数组长度,MAXnums[i] 的最大值(不超过 1e9,二进制位数约 30)。对每个数计算 suffix_ones 需要 O(log T) 时间。
  • 空间复杂度:O(1),除了返回的数组外,只使用了常数额外空间。

实现细节

  1. 计算 suffix_ones:通过循环检查最低位是否为 1,并右移,直到遇到 0。
  2. 特判 suffix_ones == 0 的情况,返回 -1。
  3. 否则,计算 x = num - (1 << (suffix_ones - 1))
  4. 注意质数除了 2 都是奇数,所以 suffix_ones 至少为 1(除了 2)。

测试用例验证

  • 示例 1:[2,3,5,7][-1,1,4,3]
  • 示例 2:[11,13,31][9,12,15]
  • 额外测试包括单个质数、全 1 的情况等,均通过。

总结

本题的关键在于发现 x OR (x+1) 的位运算性质,并将其转化为计算目标值后缀连续 1 的个数。算法简洁高效,易于实现。

代码已通过所有测试,满足题目要求。

AI 回复

解题思路分析

问题理解

题目要求构造一个数组 ans,使得对于每个索引 i,满足 ans[i] OR (ans[i] + 1) == nums[i],并且 ans[i] 尽可能小。如果不存在这样的 ans[i],则设为 -1。

这是一个位运算相关的构造问题,需要找出满足条件的最小非负整数 x

关键观察与数学推导

通过对位运算 x OR (x + 1) 的分析,发现了以下重要性质:

  1. 二进制模式分析

    • x 的最低有效 0 位位于位置 k(从 0 开始计数)
    • 那么 x 的二进制表示为:...b_{k+1} 0 111...1(低 k 位全为 1)
    • x + 1 的二进制表示为:...b_{k+1} 1 000...0(低 k 位全为 0)
  2. 位运算结果

    x OR (x + 1) = ...b_{k+1} 1 111...1
    
    • k+1 位全为 1
    • 更高位与 x 的高位相同
  3. 逆向推导

    • 给定目标值 T = nums[i]
    • 要满足 x OR (x + 1) = T,则 T 的低 k+1 位必须全为 1
    • suffix_onesT 的二进制表示中从最低位开始的连续 1 的个数
    • suffix_ones = 0 时无解(因为最低位必须为 1)
    • suffix_ones > 0 时,取 k = suffix_ones - 1 可得最小解

算法设计

基于上述推导,算法步骤如下:

  1. 对于每个 nums[i]

    • 计算从最低位开始的连续 1 的个数 suffix_ones
    • 如果 suffix_ones == 0,返回 -1
    • 否则,计算 ans[i] = nums[i] - (1 << (suffix_ones - 1))
  2. 正确性证明

    • suffix_ones = 0 时,nums[i] 的最低位为 0,无法满足条件
    • suffix_ones > 0 时,x = nums[i] - (1 << (suffix_ones - 1)) 恰好将第 (suffix_ones-1) 位清零
    • 验证:x OR (x + 1) 会将低 suffix_ones 位全部置为 1,正好等于 nums[i]

复杂度分析

  • 时间复杂度O(n log MAX),其中 n 是数组长度,MAXnums[i] 的最大值(不超过 1e9,二进制位数约 30)
  • 空间复杂度O(1),除了返回的数组外,只使用了常数额外空间

实现策略

代码中提供了三种实现策略,核心思想相同:

  1. Count Suffix Ones:直接计算后缀连续 1 的个数
  2. Bit Mask:使用位掩码清零相应位
  3. Alternative:与策略 1 类似,展示不同写法

测试用例设计

除了题目提供的示例外,还添加了多种边界情况测试:

  • 单个质数 2(无解情况)
  • 单个质数 3, 5, 7(基本验证)
  • 较大的质数如 131, 257(验证算法正确性)
  • 全 1 的二进制数 1023(最大后缀连续 1 情况)

关键点总结

  1. 位运算性质:理解 x OR (x + 1) 的二进制模式是解题关键
  2. 后缀连续 1:问题转化为计算目标值的后缀连续 1 的个数
  3. 最小化构造:通过清零最高位的后缀 1 来得到最小解
  4. 无解判断:当目标值最低位为 0 时无解

该算法充分利用了位运算的特性,通过简单的位操作即可高效解决问题,避免了暴力搜索,具有良好的时间和空间效率。

✅ 解题完成!代码已通过编译和测试。


本报告由 AI 自动生成,包含完整的思考过程和解题步骤。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant